using System;
using System.Reflection;
using System.IO;

namespace tvgrabau
{
	public class GrabberManager
	{
		string[] pluginAssemblies; 
		Assembly a;
		Type[] plugin;
		bool canWork = false;
		string TVGADir  = "";
	
		public class PluginInfo
		{
			public string Name;
			public string Author;
			public string Version;
		}
			
		public GrabberManager(string TVGADir)
		{
			// When we create a new instance of the
			// GrabberManager class, go ahead and look
			// for all the plugins in the plugins dir
			
			
			if (!Directory.Exists(TVGADir + "plugins"))
			{
			    if (MainClass.loud)
			    {
        			Console.WriteLine("[WARNING]: No 'plugins' directory found. Creating...");
        	    }
				Directory.CreateDirectory(TVGADir + "plugins");
		    }
		    
		    bool passed = checkPluginDir(TVGADir + "plugins");              // Check plugins dir.
			if (!passed) { checkPluginDir("."); }                           // Check current dir.
			if (!passed) { checkPluginDir("/usr/lib/tvgrabau", true); }     // Should be in here on *nix systems.
			
			this.TVGADir = TVGADir;
		}
		
		public bool checkPluginDir(string path)
		{
		    return checkPluginDir(path, false);
		}
		
		public bool checkPluginDir(string path, bool die)
		{
		    pluginAssemblies = Directory.GetFiles(path, "*.dll");
		    if (pluginAssemblies.Length == 0)
		    {
		        if (die)
		        {
		            if (MainClass.loud)
		            {
		                Console.WriteLine("[ERROR]: No suitable grabber plugins found!");
		            }
					Environment.Exit(3);
					return false;   // We can't *actually* get here; make the compiler happy.
		        }
		        else
		        {
		            return false;
		        }
		    }
		    else
		    {
		        return true;
		    }
		}
		
		public bool selectPlugin()
		{
			return selectPlugin(null);
		}
		
		public bool selectPlugin(string pname)
		{
			//Console.WriteLine("Trying {0}...", pname);
			bool didLoad = false;
			string usedname = "";
			try
			{
				if (pname == null || pname.Length == 0)
				{
					a = Assembly.LoadFrom(pluginAssemblies[0]);
					plugin = a.GetTypes();
					didLoad = true;
					usedname = pluginAssemblies[0];
				}
				else
				{
					int x = 0;
					foreach (string pa in pluginAssemblies)
					{
						string p = System.IO.Path.GetFileName(pa);
						if (p == pname)
						{
							a = Assembly.LoadFrom(pluginAssemblies[x]);
							plugin = a.GetTypes();
							didLoad = true;
							usedname = pluginAssemblies[x];
						}
						
						x++;
					}
				}
			}
			catch (System.Exception ex)
			{
			    if (MainClass.loud)
			    {
    				if (!didLoad)
    				{
    					Console.WriteLine("[ERROR]: Cannot load plugin - specified plugin was not found.");
    				}
    				else
    				{
    					Console.WriteLine("Cannot load plugin - "+ex.Message);
    				}
				}
				return false;
			}
			finally
			{
				if (didLoad)
				{
				    if (usedname.IndexOf(TVGADir) == -1)    // Not in ~/.tvgrabau/plugins
				    {
				        // Copy it in if it doesn't already exist.
				        if (!File.Exists(TVGADir + "plugins/" + pname))
				        {
				            if (MainClass.loud)
				            {
				                Console.WriteLine("[INFO]: Copied plugin {0} to plugin directory.", pname);
				            }
				            File.Copy(usedname, TVGADir + "plugins/" + pname);
				        }
				    }
				    
					canWork = true;
				}
			}
			
			return true;
		}
		
		public PluginInfo GetInfo()
		{
			if (canWork)
			{
				PluginInfo gi = new PluginInfo();
				
				gi.Version = a.GetName().Version.ToString();
				gi.Name = a.GetName().Name.ToString();
				// TODO: Implement Author retrieval 
				
				return gi;
			}
			else
			{
				throw new System.InvalidOperationException("Cannot retrieve plugin information - no plugin was loaded.");
				//return null;
			}
		}
		
		public void PerformFetch()
		{
			_invokeMethod("Fetch");
		}
		
		public void PrintHelp()
		{
			_invokeMethod("PrintHelp");
		}
		
		
		public void ListChannels()
		{
			_invokeMethod("ListChannels");
		}
		
		public void Configure()
		{
			_invokeMethod("Configure");
		}
		
		public void PrintCaps()
		{
			_invokeMethod("Capabilities");
		}
		
		public void PrintDesc()
		{
			_invokeMethod("Description");
		}

		private void _invokeMethod(string methodName)
		{
			if (canWork)
			{
				foreach (Type t in plugin)
				{
					MethodInfo boot = t.GetMethod(methodName);
					if (boot == null)
						continue;

					if (!boot.IsStatic)
						continue;
						
					boot.Invoke (null, new Type [0]);
					/*if (MainClass.ReturnCode == -1)
					{
						Console.WriteLine("Invoke failed - method not found! Please report that \""+methodName+"\" was not found in the currently selected plugin, to the plugin author.");
					}
					else if (MainClass.ReturnCode == -2)
					{	
						Console.WriteLine("Invoke failed - function is still to be implemented!");
					}*/
					
					//MainClass.ReturnCode = -1;

					//boot.Invoke(null, tt);
				}
			}
			else
			{
				throw new System.InvalidOperationException("Cannot invoke any external methods - no plugin was loaded.");
			}
		}
	}
}
